//
//  1v1PrebuiltViewController.swift
//  ZegoUIKitExample
//
//  Created by zego on 2022/7/14.
//

import UIKit
import SnapKit
import Kingfisher
import Reusable
import GKCover
import Localize_Swift

// 通话等待
class ZegoAudioCallWaitView : UIView {
    
    lazy var backgroundImage: UIImageView = {
        let bgImage = UIImageView()
        bgImage.contentMode = .scaleAspectFill
        return bgImage
    }()
    
    lazy var callStatusLabel: UILabel = {
        let label = UILabel()
        label.frame.size = CGSize(width: 100, height: 23)
        label.frame.origin.y = self.audioUserNameLabel.frame.origin.y + self.audioUserNameLabel.frame.size.height + 25
        label.center.x = UIScreen.main.bounds.width / 2
        label.font = UIFont.systemFont(ofSize: 16)
        label.textColor = UIColor.white
        label.textAlignment = .center
        return label
    }()
    
    lazy var audioUserIconLabel: UILabel = {
        let topPadding: CGFloat = xIphoneStatusBarHeight//UIApplication.shared.keyWindow?.safeAreaInsets.top ?? 0
        let label = UILabel()
        label.backgroundColor = UIColor.colorWithHexString("#DBDDE3")
        label.frame.size = CGSize(width: 100, height: 100)
        label.frame.origin.y = topPadding + 138
        label.center.x = UIScreen.main.bounds.width / 2
        label.clipsToBounds = true
        label.layer.cornerRadius = 50
        label.font = UIFont.systemFont(ofSize: 23)
        label.textColor = UIColor.black
        label.textAlignment = .center
        return label
    }()
    
    lazy var audioUserNameLabel: UILabel = {
        let label = UILabel()
        label.font = UIFont.systemFont(ofSize: 21)
        label.frame.size = CGSize(width: 200, height: 30)
        label.frame.origin.y = self.audioUserIconLabel.frame.origin.y + self.audioUserIconLabel.frame.size.height + 5
        label.center.x = UIScreen.main.bounds.width / 2
        label.textColor = UIColor.white
        label.textAlignment = .center
        return label
    }()
    
    lazy var userAvatarImage: UIImageView = {
        let userImage: UIImageView = UIImageView()
        userImage.clipsToBounds = true
        userImage.layer.cornerRadius = 50
        userImage.isHidden = true
        return userImage
    }()
    
    public init(frame: CGRect, userName:String, bgImage:UIImage, callingString:String) {
        super.init(frame: frame)
        addSubview(backgroundImage)
        addSubview(audioUserIconLabel)
        addSubview(audioUserNameLabel)
        addSubview(callStatusLabel)
        addSubview(userAvatarImage)
        audioUserIconLabel.text = String(userName.prefix(1))
        audioUserNameLabel.text = userName
        callStatusLabel.text = callingString
        backgroundImage.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height)
        backgroundImage.image = bgImage
        userAvatarImage.frame = audioUserIconLabel.frame
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
}

// 通话预览
@objcMembers
open class ZegoUIKitPrebuiltCallVC: UIViewController {
    
    var bottomBarHeight: CGFloat = adaptLandscapeHeight(62) + UIKitBottomSafeAreaHeight
    var topMenuBarHeight: CGFloat {
        xIphoneNavStatusBarHeight
    }
    
    public weak var delegate: ZegoUIKitPrebuiltCallVCDelegate?
    
    private let help: ZegoUIKitPrebuiltCallVC_Help = ZegoUIKitPrebuiltCallVC_Help()
    fileprivate var config: ZegoUIKitPrebuiltCallConfig = ZegoUIKitPrebuiltCallConfig.oneOnOneVideoCall()
    private var userID: String?
    private var userName: String?
    private var roomID: String?
    private var timerCount: Int = 3
    private var currentBottomMenuBar: UIView?
    private var bottomBarY: CGFloat = 0
    private var topBarY: CGFloat = 0
    var lastFrame: CGRect = CGRect.zero
    let callDuration: ZegoCallDuration = ZegoCallDuration()
    
    /// true: 需要添加翻译，并显示实时翻译
    var isNeedTranslation: Bool {
        callInvitationData.customData == "1" || callInvitationData.customData == "223"
    }
    
    /// true: 我是创建通话的人，false：我是被邀请通话的人
    var isBuilder: Bool {
        callInvitationData.inviter?.userID == String(UserProfile.userId)
    }
    
    /// 语音转写服务类型：true-科大讯飞，false-阿里（NeoNui）
    var useIFlyASR: Bool = true
    
    /// 科大讯飞实时语音转写管理器
    private var iflyASRManager: IFlyRealTimeASRManager?
    
    /// 阿里语音转写管理器（现有的TranslationManager）
    private var translationManager: TranslationManager?
    
    lazy var callRoomForegroundBaseView: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.backgroundColor = .clear
        return view
    }()
    
    // 视频窗口
    lazy var avContainer: ZegoAudioVideoContainer = {
        let container: ZegoAudioVideoContainer = ZegoAudioVideoContainer()
        container.delegate = self.help
        return container
    }()
    
    // 底部工具栏
    lazy var menuBar: ZegoCallBottomMenuBar = {
        let menuBar = ZegoCallBottomMenuBar()
        menuBar.showQuitDialogVC = self
        menuBar.config = self.config
        menuBar.delegate = self
        menuBar.backgroundColor = UIColor.colorWithHexString("#222222", alpha: 0.9)
        return menuBar
    }()
    
    // 底部工具栏，包含挂断，翻转摄像头等
    lazy var lightMenuBar: ZegoCallBottomMenuBar = {
        let menuBar = ZegoCallBottomMenuBar()
        menuBar.showQuitDialogVC = self
        menuBar.config = self.config
        menuBar.delegate = self
        return menuBar
    }()
    
    lazy var topBar: ZegoTopMenuBar = {
        let topMenuBar = ZegoTopMenuBar()
        topMenuBar.isHidden = false//!self.config.topMenuBarConfig.isVisible
        topMenuBar.backgroundColor = .clear
        topMenuBar.showQuitDialogVC = self
        topMenuBar.config = self.config
        topMenuBar.delegate = self
        return topMenuBar
    }()
    
    // 倒计时
    lazy var callTimeLabel: UILabel = {
        let label: UILabel = UILabel()
        label.font = UIFont.systemFont(ofSize: 14, weight: .regular)
        label.textAlignment = .center
        label.textColor = UIColor.white
        label.isHidden = !self.config.showCallDuration
        return label
    }()
    
    // 添加翻译入口
    lazy var addTranslationView: UIView = {
        let view = UIView()
        view.backgroundColor = .black.setAlpha(0.3)
        view.sg.addTapGesture(self, #selector(onTapAddUser))
        view.addRoundedCorners(radius: 10)
        view.isHidden = true
        
        let subview = UIView()
        subview.backgroundColor = .themeColor
        subview.addRoundedCorners(radius: 14)
        view.addSubview(subview)
        subview.snp.makeConstraints { make in
            make.edges.equalTo(UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8))
        }
        
        let label = UILabel()
        label.text = "添加人工翻译".localized(.call)
        label.textColor = .white
        label.font = .systemFont(ofSize: 12)
        subview.addSubview(label)
        label.snp.makeConstraints { make in
            make.edges.equalTo(UIEdgeInsets(top: 0, left: 8, bottom: 0, right: 8))
        }
        return view
    }()
    
    // 翻译进入的时间，移除翻译
    lazy var translationTimeView: TranslationShowTimeView = {
        let view = TranslationShowTimeView.loadFromNib()
        view.isHidden = true
        view.removeBlock = { [weak self] in
            self?.removeTranslation()
        }
        return view
    }()
    
    // 翻译正在加入
    lazy var translationEnteringView: UIView = {
        let view = UIView()
        view.backgroundColor = .black.setAlpha(0.3)
        view.addRoundedCorners(radius: 10)
        view.isHidden = true
        
        let label = UILabel()
        label.text = "翻译正在赶来的路上。。。".localized(.call)
        label.textColor = .white
        label.font = .systemFont(ofSize: 12)
        view.addSubview(label)
        label.snp.makeConstraints { make in
            make.edges.equalTo(UIEdgeInsets(top: 0, left: 8, bottom: 0, right: 8))
        }
        return view
    }()
    
    // 翻译窗口
//    lazy var translationView: UITableView = {
//        let tableView = UITableView()
//        tableView.delegate = self
//        tableView.dataSource = self
//        tableView.register(UINib.init(nibName: "TransMessageCell", bundle: Bundle.main), forCellReuseIdentifier: "TransMessageCell")
//        tableView.backgroundColor = .clear
//        tableView.separatorStyle = .none
//        tableView.showsVerticalScrollIndicator = false
//        return tableView
//    }()
    
    lazy var oneToOneTranslationView: CallTranslationView = {
        CallTranslationView.loadFromNib()
    }()
    
    
    
    var waitingView: ZegoAudioCallWaitView?
    
    var messages: [IMRecvSeiModel] = NSMutableArray() as! [IMRecvSeiModel]
    
    /// 通话时间，单位：秒
    var callTimeDuration: Int = 0
    /// 翻译进入直播间的时间
    var translatorsDuration: Int = 0
    /// 被邀请的翻译
    var invitationTranslatorsModel: TranslatorsModel?
    /// 翻译的 id
    var translatorId: String? = nil
    
    /// 录制任务ID
    var taskId: String? = nil
    
    /// 翻译列表
    lazy var translationListView: TranslationListView = {
        let view = TranslationListView.loadFromNib()
        view.didSelectedTranslationBlock = { [weak self] model in
            if let model = model {
                // 等待翻译进入
                xLog("邀请翻译加入通话 \(model.name)")
                // 要弱引用，容易循环引用，无法销毁
                self?.invitationTranslatorsModel = model
                self?.translatorId = String(model.userId)
                self?.watingTranslationEntering()
                
                ZegoUIKitCore.shared.sendSEI([
                    "type": "3",
                    "userID": String(model.userId),
                    "username": String(model.name)
                ])
                
                // 邀请翻译加入房间
                let callUser = ZegoPluginCallUser(userID: String(model.userId), userName:model.name, avatar: model.avatar)
                let notificationConfig = ZegoSignalingPluginNotificationConfig(resourceID: "gluz_call_resources")
                ZegoUIKitPrebuiltCallInvitationService.shared.sendCallingInvitationNoStartCall([callUser], invitationType: .videoCall, timeout: 60, customerData: "0", notificationConfig: notificationConfig) { [weak self] data in
                    guard let dic = data else { return }
                    xLog("sendCallingInvitationNoStartCall data \(dic as NSDictionary) ")
                    let code = dic["code"] as! Int
                    if code != 0 {
                        xLog("邀请失败 \(code)")
                        self?.deleteTranslation()
                    }
                }
            }
            GKCover.hide()
        }
        return view
    }()
    
    var callInvitationData: ZegoCallInvitationData
    /// 初始化调用页
    /// - Parameters:
    ///   - data: Call invitation data
    ///   - config: call personalized configuration
    public init(_ data: ZegoCallInvitationData, config: ZegoUIKitPrebuiltCallConfig?) {
        callInvitationData = data
        super.init(nibName: nil, bundle: nil)
        self.help.callVC = self
        ZegoUIKit.shared.addEventHandler(self.help)
        userID = ZegoUIKit.shared.localUserInfo?.userID
        userName = ZegoUIKit.shared.localUserInfo?.userName
        roomID = data.callID
        xLog("初始话页面")
        xLog("userName: \(data.inviter?.userName ?? "")")
        xLog("userID: \(data.inviter?.userID ?? "")")
        xLog("invitationID: \(data.invitationID ?? "")")
        
        showTranslation(isNeedTranslation)
        
        
        /**
         开启悬浮窗还需要在 info.plist 中添加 background modes, 并设置 audio、fetch、processing、push、remote、voice
         */
        
        // 需要悬浮窗口的话,打开注释代码
        // ZegoUIKitPrebuiltCallInvitationService.shared.delegate = self
        if let config = config {
            ZegoUIKit.shared.setVideoConfig(config: config.videoConfig.resolution)
            // 需要悬浮窗口的话,打开注释代码
            // config.topMenuBarConfig.buttons = [.minimizingButton]
            self.config = config
        }
    }
    
    required public init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    open override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = UIColor.black
        // 摄像头是否默认开启。默认为启用。
        if config.turnOnCameraWhenJoining == false {
            joinRoomAudioWaitingView()
        }
        
        view.addSubview(avContainer.view)
        avContainer.view.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
        
        view.addSubview(callTimeLabel)
        callTimeLabel.snp.makeConstraints { make in
            make.centerX.equalToSuperview()
            make.top.equalToSuperview().offset(xIphoneStatusBarHeight)
            make.height.equalTo(32)
        }
        
        view.addSubview(addTranslationView)
        addTranslationView.snp.makeConstraints { make in
            make.left.equalTo(16)
            make.top.equalTo(callTimeLabel.snp.bottom).offset(16)
            make.height.equalTo(44)
        }
        
        view.addSubview(translationTimeView)
        translationTimeView.snp.makeConstraints { make in
            make.left.equalTo(16)
            make.top.equalTo(callTimeLabel.snp.bottom).offset(16)
            make.height.equalTo(70)
        }
        
        view.addSubview(translationEnteringView)
        translationEnteringView.snp.makeConstraints { make in
            make.left.equalTo(16)
            make.top.equalTo(callTimeLabel.snp.bottom).offset(16)
            make.height.equalTo(44)
        }
        
        
        view.addSubview(topBar)
        if config.bottomMenuBarConfig.style == .dark {
            currentBottomMenuBar = menuBar
            bottomBarHeight = adaptLandscapeHeight(104) + UIKitBottomSafeAreaHeight
            view.addSubview(menuBar)
        } else {
            currentBottomMenuBar = lightMenuBar
            bottomBarHeight = adaptLandscapeHeight(61) + UIKitBottomSafeAreaHeight
            view.addSubview(lightMenuBar)
        }
        
        if let callRoomForegroundView = delegate?.requireRoomForegroundView?() {
            callRoomForegroundBaseView.addSubview(callRoomForegroundView)
            callRoomForegroundView.snp.makeConstraints { make in
                make.edges.equalToSuperview()
            }
            view.insertSubview(callRoomForegroundBaseView, aboveSubview: avContainer.view!)
        }
        
        ZegoMinimizeManager.shared.delegate = self
        ZegoMinimizeManager.shared.pipConfig = config.layout.config
        if config.topMenuBarConfig.buttons.contains(.minimizingButton) || config.bottomMenuBarConfig.buttons.contains(.minimizingButton) {
            if config.turnOnCameraWhenJoining && config.layout.mode == .pictureInPicture {
                ZegoMinimizeManager.shared.setupPipControllerWithSourceView(sourceView: view, isOneOnOneVideo: true)
            } else {
                ZegoMinimizeManager.shared.setupPipControllerWithSourceView(sourceView: view, isOneOnOneVideo: false)
            }
        }
        
        // 增加翻译窗口
        view.addSubview(oneToOneTranslationView)
        oneToOneTranslationView.snp.makeConstraints { make in
            make.left.right.equalToSuperview()
            make.bottom.equalTo(-bottomBarHeight)
            make.height.lessThanOrEqualTo(xScreenH*0.5)
        }
        
        if UserProfile.identityId != 11 {
            // 非翻译才加载翻译数据
            translationListView.initData()
        }
        setupLayout()
        joinRoom()
    }
    
    open override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        UIApplication.sg.isIdleTimerDisabled(true)
    }
    
    open override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        UIApplication.sg.isIdleTimerDisabled(false)
    }
    
    open override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        if !self.view.frame.equalTo(self.lastFrame) {
            // self.avContainer.view.frame = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height)
            self.topBar.frame = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.topMenuBarHeight)
            self.currentBottomMenuBar?.frame = CGRect.init(x: 0, y: self.view.frame.size.height - self.bottomBarHeight, width: self.view.frame.size.width, height: self.bottomBarHeight)
            self.menuBar.addCorner(conrners: [.topLeft,.topRight], radius: 16)
            self.lastFrame = self.view.frame
        }
    }
    
    func setupLayout() {
        let audioVideoConfig: ZegoAudioVideoViewConfig = ZegoAudioVideoViewConfig()
        audioVideoConfig.showSoundWavesInAudioMode = self.config.audioVideoViewConfig.showSoundWavesInAudioMode
        audioVideoConfig.useVideoViewAspectFill = self.config.audioVideoViewConfig.useVideoViewAspectFill
        self.avContainer.setLayout(self.config.layout, audioVideoConfig: audioVideoConfig)
        ZegoUIKit.shared.setAudioOutputToSpeaker(enable: self.config.useSpeakerWhenJoining)
        
        if config.bottomMenuBarConfig.style == .dark {
            self.currentBottomMenuBar?.backgroundColor = UIColor.colorWithHexString("#222222", alpha: 0.8)
        } else {
            self.currentBottomMenuBar?.backgroundColor = UIColor.clear
        }
    }
    
    
    /// 滚动到最下
    func scrollToBottom() {
        
//        UIView.animate(withDuration: 0, delay: 1.0, options: .allowUserInteraction, animations: {
//            
//        }) { (finished) in
//            
//            if self.translationView.isTracking ||
//                self.translationView.isDragging ||
//                self.translationView.isDecelerating {
//                return
//            }
//            
//            let index = self.messages.count - 1
//            if index >= 0 {
//                self.translationView.scrollToRow(at: IndexPath(row: index, section: 0), at: .bottom, animated: true)
//            }
//        }
    }
    
    
    private func joinRoom() {
        guard let roomID = self.roomID,
              let userID = self.userID,
              let userName = self.userName
        else {
            xLog("ss")
            return
        }
        ZegoUIKit.shared.joinRoom(userID, userName: userName, roomID: roomID) {[weak self] code in
            guard let self = self else { return }
            xLog("加入房间 code \(code) userID:\(userID)  userName:\(userName) roomID:\(roomID)")
            if code == 0 {
                // ZegoUIKit.shared.turnCameraOn(userID, isOn: self.config.turnOnCameraWhenJoining)
                // 根据等待页面是否关闭摄像头来设定，自定义
                ZegoUIKit.shared.turnCameraOn(userID, isOn: ZegoUIKitPrebuiltCallProfile.shared.isSwitchCamera)
                ZegoUIKit.shared.turnMicrophoneOn(userID, isOn: self.config.turnOnMicrophoneWhenJoining)
                ZegoUIKit.shared.startPreview(self.view, videoMode: .aspectFill)
                callDuration.delegate = self
                callDuration.startTheTimer()
                if isNeedTranslation {
                    realtimeTranslation(true)
                }
                
                // 新增：调用音视频通话开始接口
                callStartRoomRecordingAPI()
            }
        }
    }
    
    private func joinRoomAudioWaitingView() {
        let topPadding: CGFloat = xIphoneStatusBarHeight // UIApplication.shared.keyWindow?.safeAreaInsets.top ?? 0
        let bottomPadding: CGFloat = xIphoneBottomSafeHeight // UIApplication.shared.keyWindow?.safeAreaInsets.bottom ?? 0
        self.waitingView = ZegoAudioCallWaitView.init(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height + topPadding + bottomPadding), userName: self.userName ?? "",bgImage: ZegoUIKitCallIconSetType.call_waiting_bg.load(), callingString: self.config.zegoCallText.outgoingAudioCallPageMessage)
        let user: ZegoUIKitUser = ZegoUIKitUser(ZegoUIKit.shared.localUserInfo?.userID ?? "", ZegoUIKit.shared.localUserInfo?.userName ?? "")
        let userAvatar:String = (ZegoUIKitPrebuiltCallInvitationService.shared.delegate?.onUserIDUpdated?(user: user) ?? "") as String
        if let uRl = URL(string: userAvatar) {
            self.waitingView?.userAvatarImage.isHidden = false
            self.waitingView?.audioUserIconLabel.isHidden = true
            self.waitingView?.userAvatarImage.kf.setImage(with: uRl)
        }
        self.view.addSubview(self.waitingView!)
    }
    
    
    // 添加翻译
    @objc func onTapAddUser(){
        translationListView.reloadUI()
        GKCover.cover(
            from: view,
            contentView: translationListView,
            style: .translucent,
            showStyle: .bottom,
            showAnimStyle: .none,
            hideAnimStyle: .none,
            notClick: false
        )
    }
    
    
    deinit {
        stopRealtimeTranslation()
        callDuration.stopTheTimer()
        ZegoUIKit.shared.removeEventHandler(self.help)
        ZegoUIKit.shared.leaveRoom()
        xLog("ZegoUIKitPrebuiltCallVC deinit")
    }
}

// MARK: - 翻译进来 UI 改变事件
private extension ZegoUIKitPrebuiltCallVC {
    
    /// 是否显示添加翻译入口
    /// - Parameter isShow: true：有入口
    func showTranslation(_ isShow: Bool) {
        if isBuilder {
            addTranslationView.isHidden = !isShow
        } else {
            // 非创建者不显示添加翻译入口
            addTranslationView.isHidden = true
        }
    }
    
    // 我自己是翻译
    func myselfIsTranslation() {
        // 显示正在等待翻译进入
        translationEnteringView.isHidden = true
        // 隐藏添加翻译入口
        addTranslationView.isHidden = true
        // 隐藏翻译的服务时长
        translationTimeView.isHidden = true
        
        stopRealtimeTranslation()
    }
    
    /// 等翻译进来
    func watingTranslationEntering() {
        // 显示正在等待翻译进入
        translationEnteringView.isHidden = false
        // 隐藏添加翻译入口
        addTranslationView.isHidden = true
        // 隐藏翻译的服务时长
        translationTimeView.isHidden = true
    }
    
    /// 翻译来了
    func translationAlreadyCome() {
        // 记录翻译来的时间点
        translatorsDuration = callTimeDuration
        // 隐藏翻译正在进入的 view
        translationEnteringView.isHidden = true
        // 隐藏添加翻译入口
        addTranslationView.isHidden = true
        // 翻译加入房间的服务时长，移除翻译按钮
        translationTimeView.isHidden = false
        // 关闭实时翻译
        realtimeTranslation(false)
    }
    
    // 我是被邀请者,翻译进来了
    func translationAlreadyComeNoBuilder() {
        // 记录翻译来的时间点
        translatorsDuration = 0
        // 隐藏翻译正在进入的 view
        translationEnteringView.isHidden = true
        // 隐藏添加翻译入口
        addTranslationView.isHidden = true
        // 翻译加入房间的服务时长，移除翻译按钮
        translationTimeView.isHidden = true
        // 关闭实时翻译
        realtimeTranslation(false)
    }
    
    // 我是被邀请者, 翻译离开了
    func translationLeaveNoBuilder() {
        // 翻译离开了
        translatorId = nil
        translatorsDuration = 0
        // 隐藏翻译正在进入的 view
        translationEnteringView.isHidden = true
        // 翻译加入房间的服务时长，移除翻译按钮
        translationTimeView.isHidden = true
        showTranslation(isNeedTranslation)
        realtimeTranslation(isNeedTranslation)
    }
    
    // 翻译离开了
    func deleteTranslation() {
        // 翻译离开了
        translatorId = nil
        translatorsDuration = 0
        // 隐藏翻译正在进入的 view
        translationEnteringView.isHidden = true
        // 翻译加入房间的服务时长，移除翻译按钮
        translationTimeView.isHidden = true
        showTranslation(isNeedTranslation)
        realtimeTranslation(isNeedTranslation)
    }
    
    /// 移除翻译
    func removeTranslation() {
        YLAlert(controller: self, title: "提示".localized(), message: "确认停止翻译客服提供服务吗？".localized(.call), actionTitles: ["确定"]) { action, index in
            guard let model = self.invitationTranslatorsModel else { return }
            
            xLog("移除翻译")
            self.invitationTranslatorsModel = nil
            ZegoUIKitCore.shared.sendSEI([
                "type": "2",
                "userID": String(model.userId),
                "username": String(model.name)
            ])
        }
    }
    
    // 关闭视频通话
    func leaveVideoCall() {
        // 新增：调用音视频通话结束接口
        callStopRecordingAPI()
        
        // 把我踢出去了，我主动退出
        xLog("通话结束")
        let endEvent: ZegoCallEndEvent = ZegoCallEndEvent()
        // 视频通话事件结束原因，kickOut：被踢
        endEvent.reason = .kickOut
        // 将您踢出的用户ID
        endEvent.kickerUserID = ZegoUIKitPrebuiltCallInvitationService.shared.userID ?? ""
        xLog("将您踢出的用户ID: \(endEvent.kickerUserID)")
        self.delegate?.onCallEnd?(endEvent)
        
        ZegoUIKitCore.shared.sendSEI([
            "type": "4",
            "userID": String(UserProfile.userId),
            "username": String(UserProfile.username)
        ])
        
        self.dismiss(animated: true)
        ZegoUIKit.shared.leaveRoom()
        ZegoCallAudioPlayerTool.stopPlay()
        guard let invitationData = ZegoUIKitPrebuiltCallInvitationService.shared.invitationData else { return }
        xLog("invitationData \(invitationData.callID ?? "")")
        var needCancel: Bool = true
        if let invitees = ZegoUIKitPrebuiltCallInvitationService.shared.invitees {
            xLog("遍历视频通话内的成员")
            var cancelInvitees: [String] = []
            // 如果我自己是邀请者
            if invitationData.inviter?.userID == ZegoUIKit.shared.localUserInfo?.userID {
                // 遍历成员列表
                for user in invitees {
                    if user.state == .accept {
                        needCancel = false
                    }
                    cancelInvitees.append(user.user?.userID ?? "")
                }
            }
            if needCancel {
                ZegoUIKitSignalingPluginImpl.shared.cancelInvitation(cancelInvitees, data: nil, callback: nil)
            }
        }
        ZegoUIKitPrebuiltCallInvitationService.shared.invitationData = nil
        ZegoUIKitPrebuiltCallInvitationService.shared.callID = nil
    }
}

// MARK: - 实时翻译
extension ZegoUIKitPrebuiltCallVC {
    
    // 开启实时传译
    func startRealtimeTranslation() {
        xLog("开启实时传译")
        if UserProfile.identityId == 11 {
            // 我自己是翻译,不开实时传译
            xLog("我自己是翻译,不开实时传译")
            return
        }
        
        if useIFlyASR {
            // 使用科大讯飞实时语音转写
            startIFlyASR()
        } else {
            // 使用阿里语音转写（现有方式）
            startAliASR()
        }
    }
    
    // 开启科大讯飞实时语音转写
    private func startIFlyASR() {
        xLog("开启科大讯飞实时语音转写")
        
        iflyASRManager = IFlyRealTimeASRManager.shared
        iflyASRManager?.delegate = self
        iflyASRManager?.currentLanguage = LanguageManager.isCountryCN ? .chinese : .russian
        
        // 开始音频数据采集
        startAudioDataObserver()
        
        // 开始识别
        let success = iflyASRManager?.startRealTimeASR() ?? false
        if success {
            xLog("科大讯飞实时语音转写启动成功")
        } else {
            xLog("科大讯飞实时语音转写启动失败")
        }
    }
    
    // 开始音频数据采集
    private func startAudioDataObserver() {
        // 需要的音频数据类型 Bitmask，此处示例四个回调都开启
        let bitmask: ZegoAudioDataCallbackBitMask = [.captured, .player]
        // 需要的音频数据参数，此处示例单声道、16 K
        let param = ZegoAudioFrameParam()
        param.channel = .mono
        param.sampleRate = .rate16K
        
        // 开启获取原始音频数据功能
        ZegoExpressEngine.shared().startAudioDataObserver(bitmask, param: param)
        // 设置音频数据回调
        ZegoExpressEngine.shared().setAudioDataHandler(self)
    }
    
    // 停止音频数据采集
    private func stopAudioDataObserver() {
        // 异步停止，避免阻塞调用线程
        DispatchQueue.global(qos: .userInitiated).async {
            ZegoExpressEngine.shared().stopAudioDataObserver()
        }
    }
    
    // 开启阿里语音转写
    private func startAliASR() {
        xLog("开启阿里语音转写")
        
        translationManager = TranslationManager.shared
        translationManager?.fromLanguage = LanguageManager.isCountryCN ? "zh" : "ru"
        translationManager?.toLanguage = LanguageManager.isCountryCN ? "ru" : "zh"
        translationManager?.realtimeTranslationBlock = { [weak self] model in
            DispatchQueue.main.async {
                self?.handleTranslationResult(model)
            }
        }
        
        // 开始音频采集和翻译
        translationManager?.startAudioDataObserver()
        translationManager?.startEventHandler()
        translationManager?.startRealtimeTranslation()
    }
    
    // 结束实时传译
    func stopRealtimeTranslation() {
        xLog("关闭实时翻译")
        
        if useIFlyASR {
            // 停止科大讯飞实时语音转写
            stopIFlyASR()
        } else {
            // 停止阿里语音转写
            stopAliASR()
        }
    }
    
    // 停止科大讯飞实时语音转写
    private func stopIFlyASR() {
        xLog("停止科大讯飞实时语音转写")
        iflyASRManager?.stopRealTimeASR()
        stopAudioDataObserver()
        iflyASRManager = nil
    }
    
    // 停止阿里语音转写
    private func stopAliASR() {
        xLog("停止阿里语音转写")
        translationManager?.stopRealtimeTranslation()
        translationManager?.stopAudioDataObserver()
        translationManager = nil
    }
    
    // 处理翻译结果
    private func handleTranslationResult(_ model: IMRecvSeiModel) {
        // 将翻译结果添加到消息列表
        messages.append(model)
        
        // 更新翻译UI
        oneToOneTranslationView.model = model
        
        // 发送SEI消息给其他用户
        ZegoUIKitCore.shared.sendSEI([
            "type": "1",
            "trans": model.trans,
            "text": model.text,
            "userId": model.userId,
            "username": model.username,
            "usernameRu": model.usernameRu
        ])
    }
    
    /// 实时翻译是否启动
    /// - Parameter isStart: true：启动
    func realtimeTranslation(_ isStart: Bool) {
        if isStart {
            // 打开实时翻译功能
            startRealtimeTranslation()
            oneToOneTranslationView.isHidden = false
            
        } else {
            // 关闭实时翻译功能
            stopRealtimeTranslation()
            oneToOneTranslationView.isHidden = true
        }
    }
}

extension ZegoUIKitPrebuiltCallVC: NeoNuiDelegate {
    public func neoNuiRealtimeTranslationEnd(_ isFinish: Bool) {
        
    }
    
    public func neoNuiRealtimeTranslation(_ text: String, languageType: NeoNuiLanguageType, isEnd: Bool) {
        
        // if !isEnd { return }
        if text.isEmpty { return }
        
        xLog("neoNuiSpeechTranscriberResult: \(text)")
        
        let language = languageType == .zh ? ("zh", "ru"): ("ru", "zh")
        
        NeoNuiTranslation.translation(from: language.0, to: language.1, text: text) { [weak self] sourceText, result in
            guard let weakSelf = self else { return }
            
            let model = IMRecvSeiModel()
            model.type = "1"
            model.text = text
            model.trans = result ?? "机翻失败"
            model.userId = String(UserProfile.userId)
            model.username = UserProfile.username
            model.usernameRu = UserProfile.usernameRu
            
            // 发送sei消息
            xLog("发送实时传译结果")
            xLog("username \(model.username)")
            xLog("text \(model.text)")
            xLog("trans \(model.trans)")
            weakSelf.oneToOneTranslationView.model = model
            if let jsonString = model.toJSONString() {
                ZegoUIKitCore.shared.sendSEI(jsonString)
            }
        }
    }
    
    public func neoNuiRmsChanged(_ rms: Float) {
        
    }
    
}

// MARK: - IFlyRealTimeASRDelegate
extension ZegoUIKitPrebuiltCallVC: IFlyRealTimeASRDelegate {
    func realTimeASRDidBegin() {
        
    }
    
    func realTimeASRDidEnd() {
        
    }
    
    func realTimeASRDidFail(_ error: any Error) {
        
    }
    
    
    func realTimeASRDidReceiveResult(_ result: String, isLast: Bool) {
        xLog("科大讯飞实时语音转写结果: \(result), 是否最后结果: \(isLast)")
        
        if result.isEmpty { return }
        
        // 如果是最后结果，进行翻译
        if isLast {
            let fromLanguage = LanguageManager.isCountryCN ? "zh" : "ru"
            let toLanguage = LanguageManager.isCountryCN ? "ru" : "zh"
            
            NeoNuiTranslation.translation(from: fromLanguage, to: toLanguage, text: result) { [weak self] sourceText, translatedText in
                guard let weakSelf = self else { return }
                
                let model = IMRecvSeiModel()
                model.type = "1"
                model.text = result
                model.trans = translatedText ?? "机翻失败"
                model.userId = String(UserProfile.userId)
                model.username = UserProfile.username
                model.usernameRu = UserProfile.usernameRu
                
                // 处理翻译结果
                weakSelf.handleTranslationResult(model)
            }
        }
    }
    
    func realTimeASRDidComplete(_ error: Error?) {
        if let error = error {
            xLog("科大讯飞实时语音转写完成，错误: \(error.localizedDescription)")
        } else {
            xLog("科大讯飞实时语音转写完成")
        }
    }
    
    func realTimeASRConnectionStateChanged(_ isConnected: Bool) {
        xLog("科大讯飞实时语音转写连接状态: \(isConnected ? "已连接" : "已断开")")
    }
    
}

// MARK: - ZegoAudioDataHandler
extension ZegoUIKitPrebuiltCallVC: ZegoAudioDataHandler {
    
    public func onCapturedAudioData(_ data: UnsafePointer<UInt8>, dataLength: UInt32, param: ZegoAudioFrameParam) {
        // 本地采集音频数据，推流后可收到回调
        let sendData = Data(bytes: data, count: Int(dataLength))
        
        // 如果使用科大讯飞实时语音转写，发送音频数据
        if useIFlyASR {
            iflyASRManager?.sendAudioData(sendData)
        }
    }
    
}

// MARK: - CallVCApi
extension ZegoUIKitPrebuiltCallVC: CallVCApi {
    
    public func addButtonToBottomMenuBar(_ button: UIButton) {
        if self.config.bottomMenuBarConfig.style == .dark {
            self.menuBar.addButtonToMenuBar(button)
        } else {
            self.lightMenuBar.addButtonToMenuBar(button)
        }
    }
    
    public func addButtonToTopMenuBar(_ button: UIButton) {
        self.topBar.addButtonToMenuBar(button)
    }
    
    public func finish() {
        self.dismiss(animated: true, completion: nil)
    }
}

// MARK: - ZegoCallBottomMenuBarDelegate, ZegoTopMenuBarDelegate
extension ZegoUIKitPrebuiltCallVC: ZegoCallBottomMenuBarDelegate, ZegoTopMenuBarDelegate {
    
    func onMenuBarMoreButtonClick(_ buttonList: [UIView]) {
        let newList:[UIView] = buttonList
        let vc: ZegoCallMoreView = ZegoCallMoreView()
        vc.buttonList = newList
        self.view.addSubview(vc.view)
        self.addChild(vc)
    }
    
    // 关闭通话按钮
    func onHangUp(_ isHandup: Bool) {
        // 新增：调用音视频通话结束接口
        callStopRecordingAPI()
        
        let endEvent:ZegoCallEndEvent = ZegoCallEndEvent()
        endEvent.reason = .localHangUp
        // 将您踢出的用户ID
        endEvent.kickerUserID = ZegoUIKitPrebuiltCallInvitationService.shared.userID ?? ""
        self.delegate?.onCallEnd?(endEvent)
        
        if isHandup {
            // 创建者关闭通话后需要显示通话小记，其他不显示
            if isBuilder {
                self.dismiss(animated: true) {
                    let vc: CallNotesController = CallNotesController(self.roomID!)
                    let nav = BaseNavigationController(rootViewController: vc)
                    nav.modalPresentationStyle = .fullScreen
                    currentViewController()?.present(nav, animated: true, completion: nil)
                }
            }
            
            ZegoCallAudioPlayerTool.stopPlay()
            guard let invitationData = ZegoUIKitPrebuiltCallInvitationService.shared.invitationData else { return }
            var needCancel: Bool = true
            if let invitees = ZegoUIKitPrebuiltCallInvitationService.shared.invitees {
                var cancelInvitees: [String] = []
                if invitationData.inviter?.userID == ZegoUIKit.shared.localUserInfo?.userID {
                    for user in invitees {
                        if user.state == .accept {
                            needCancel = false
                        }
                        cancelInvitees.append(user.user?.userID ?? "")
                    }
                }
                if needCancel {
                    ZegoUIKitSignalingPluginImpl.shared.cancelInvitation(cancelInvitees, data: nil, callback: nil)
                }
            }
            ZegoUIKitPrebuiltCallInvitationService.shared.invitationData = nil
            ZegoUIKitPrebuiltCallInvitationService.shared.callID = nil
            
            if translatorId == String(UserProfile.userId) {
                // 我是翻译，我主动离开了
                translatorId = nil
                ZegoUIKitCore.shared.sendSEI([
                    "type": "4",
                    "userID": String(UserProfile.userId),
                    "username": String(UserProfile.username)
                ])
            }
        }
    }
    
    func onMinimizationButtonDidClick() {
        ZegoMinimizeManager.shared.callVC = self
        dismiss(animated: false)
    }
    
    /// 切换摄像头
    func onSwitchCameraButtonClick(_ isFrontFacing: Bool) {
        delegate?.onSwitchCameraButtonClick?(isFrontFacing)
    }
    
    /// 开关摄像头
    func onToggleCameraButtonClick(_ isOn: Bool) {
        delegate?.onToggleCameraButtonClick?(isOn)
    }
    
    // 开关麦克风
    func onToggleMicButtonClick(_ isOn: Bool) {
        delegate?.onToggleMicButtonClick?(isOn)
    }
    
    // 开关扬声器
    func onAudioOutputButtonClick(_ isSpeaker: Bool) {
        delegate?.onAudioOutputButtonClick?(isSpeaker)
    }
    
}

// MARK: - ZegoCallChatViewDelegate
extension ZegoUIKitPrebuiltCallVC: ZegoCallChatViewDelegate {
    //MARK: -ZegoCallChatViewDelegate
    func getChatViewItemView(_ tableView: UITableView, indexPath: IndexPath, message: ZegoInRoomMessage) -> UITableViewCell? {
        delegate?.getChatViewItemView?(tableView, indexPath: indexPath, message: message)
    }
    
    func getChatViewItemHeight(_ tableView: UITableView, heightForRowAt indexPath: IndexPath, message: ZegoInRoomMessage) -> CGFloat {
        delegate?.getChatViewItemHeight?(tableView, heightForRowAt: indexPath, message: message) ?? -1
    }
}

// MARK: - ZegoCallMemberListDelegate 获取成员列表
extension ZegoUIKitPrebuiltCallVC: ZegoCallMemberListDelegate {
    
    // 获取成员列表
    func getMemberListItemView(_ tableView: UITableView, indexPath: IndexPath, userInfo: ZegoUIKitUser) -> UITableViewCell? {
        delegate?.getMemberListItemView?(tableView, indexPath: indexPath, userInfo: userInfo)
    }
    
    func getMemberListViewForHeaderInSection(_ tableView: UITableView, section: Int) -> UIView? {
        delegate?.getMemberListViewForHeaderInSection?(tableView, section: section)
    }
    
    func getMemberListItemHeight(_ userInfo: ZegoUIKitUser) -> CGFloat {
        delegate?.getMemberListItemHeight?(userInfo) ?? 54
    }
    
    func getMemberListHeaderHeight(_ tableView: UITableView, section: Int) -> CGFloat {
        delegate?.getMemberListHeaderHeight?(tableView, section: section) ?? 65
    }
}

extension ZegoUIKitPrebuiltCallVC: ZegoMinimizeManagerDelegate {
    func willStopPictureInPicture() {
        if let callVC = ZegoMinimizeManager.shared.callVC, ZegoMinimizeManager.shared.isNarrow {
            ZegoMinimizeManager.shared.isNarrow = false
            currentViewController()?.present(callVC, animated: false)
            ZegoMinimizeManager.shared.callVC = nil
        }
    }
}

// MARK: - ZegoCallDurationDelegate 通话计时器
extension ZegoUIKitPrebuiltCallVC: ZegoCallDurationDelegate {
    func onTimeUpdate(_ duration: Int, formattedString: String) {
        // xLog("通话时长 formattedString: \(formattedString)")
        self.callTimeLabel.text = formattedString
        callTimeDuration = duration
        
        if translatorsDuration > 0 {
            // 有翻译在
            let dur = duration - translatorsDuration
            let second = String(dur%60).sg.prefixAddZero(2)
            let minute = String(dur/60).sg.prefixAddZero(2)
            translationTimeView.timeLabel.text = "\(minute):\(second)"
        }
        
        self.delegate?.onCallTimeUpdate?(duration)
        ZegoMinimizeManager.shared.updateCallTime(time: formattedString)
        
        if !isBuilder {
            // 非创建者不需要每 60 秒调用一次接口
            return
        }
        
        
        if duration%60 == 0 {
            // 每一分钟，调用一次
            xLog("每60s调用一次")
            let target = JavaAPI.callCalculateScore(roomId: roomID!, userId: userID!)
            YLRequest.request(target) { result in
                switch result {
                case .success(let res):
                    
                    let dataDictionary = res.dataDictionary
                    
                    let model = JsonUtil.modelFromDictionary(dataDictionary, CallCalculateModel.self)
                    
                    // true 通话结束，false 正常通话
                    let isCallEnd = model?.isCallEnd ?? false
                    // true：不管 false：积分提示不足
                    let isCallTips = model?.isCallTips ?? true
                    
                    if !isCallTips {
                        // 积分不足,弹提示框
                        let videoCallScoreNoTipsView = VideoCallScoreNoTipsView.loadFromNib()
                        videoCallScoreNoTipsView.cancelBlock = {
                            GKCover.hide()
                        }
                        videoCallScoreNoTipsView.goToPayBlock = { [weak self] in
                            GKCover.hide()
                            xLog("去充值")
                            let url = SystemEnvManager.shared.forwarderUrl + "/pages/vip/scorePoint?" + H5UrlManager.urlParameter()
                            let vc = BaseWebViewController(url: url, isNavBarHide: true)
                            self?.present(BaseNavigationController(rootViewController: vc))
                        }
                        
                        GKCover.cover(
                            from: self.view,
                            contentView: videoCallScoreNoTipsView,
                            style: .translucent,
                            showStyle: .center,
                            showAnimStyle: .none,
                            hideAnimStyle: .none,
                            notClick: true
                        )
                        
                    }
                    
                    if isCallEnd {
                        xLog("通话结束")
                        GKCover.hide()
                        
                        // 新增：调用音视频通话结束接口
                        self.callStopRecordingAPI()
                        
                        let endEvent:ZegoCallEndEvent = ZegoCallEndEvent()
                        endEvent.reason = .kickOut
                        endEvent.kickerUserID = ZegoUIKitPrebuiltCallInvitationService.shared.userID ?? ""
                        self.delegate?.onCallEnd?(endEvent)
                        
                        self.dismiss(animated: true) {
                            let vc: CallNotesController = CallNotesController(self.roomID!)
                            let nav = BaseNavigationController(rootViewController: vc)
                            nav.modalPresentationStyle = .fullScreen
                            currentViewController()?.present(nav, animated: true, completion: nil)
                        }
                        ZegoCallAudioPlayerTool.stopPlay()
                        guard let invitationData = ZegoUIKitPrebuiltCallInvitationService.shared.invitationData else { return }
                        var needCancel: Bool = true
                        if let invitees = ZegoUIKitPrebuiltCallInvitationService.shared.invitees {
                            var cancelInvitees: [String] = []
                            if invitationData.inviter?.userID == ZegoUIKit.shared.localUserInfo?.userID {
                                for user in invitees {
                                    if user.state == .accept {
                                        needCancel = false
                                    }
                                    cancelInvitees.append(user.user?.userID ?? "")
                                }
                            }
                            if needCancel {
                                ZegoUIKitSignalingPluginImpl.shared.cancelInvitation(cancelInvitees, data: nil, callback: nil)
                            }
                        }
                        ZegoUIKitPrebuiltCallInvitationService.shared.invitationData = nil
                        ZegoUIKitPrebuiltCallInvitationService.shared.callID = nil
                        ZegoUIKit.shared.leaveRoom()
                    }
                    
                case .failure(let err):
                    YLHUD.showHint(.error, err.errMsg)
                }
            }
            
            
        }
    }
}

// MARK: - 语音实时转文字，翻译
extension ZegoUIKitPrebuiltCallVC: UITableViewDelegate, UITableViewDataSource {
    
    // 实现数据源方法
    public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        messages.count
    }
    
    public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell:TransMessageCell = tableView.dequeueReusableCell(withIdentifier: "TransMessageCell", for: indexPath) as! TransMessageCell
        let model = messages[indexPath.row]
        cell.seiModel = model
        cell.selectionStyle = .none
        return cell
    }
}

// MARK: - ZegoUIKitPrebuiltCallVC_Help
class ZegoUIKitPrebuiltCallVC_Help: NSObject, ZegoAudioVideoContainerDelegate, ZegoUIKitEventHandle {
    
    weak var callVC: ZegoUIKitPrebuiltCallVC?
    
    func onRemoteUserJoin(_ userList: [ZegoUIKitUser]) {
        // 有成员加入进来
        guard let vc = callVC else { return }
        xLog("加入的成员列表 begin")
        
        if vc.translatorId == String(UserProfile.userId) {
            // 我自己是翻译
            xLog("我自己是翻译")
            vc.myselfIsTranslation()
            return
        }
        
        // 别人是翻译
        if vc.isBuilder {
            // 我是构建者
            // 拿到视频通话中所有得翻译
            let translators = userList.filter { $0.userID == vc.translatorId }
            if translators.count > 0 {
                // 翻译进来了
                xLog("翻译进来了")
                vc.translationAlreadyCome()
            }
        } else {
            // 我是被邀请者
            let translators = userList.filter { $0.userID == vc.translatorId }
            if translators.count > 0 {
                // 翻译进来了
                xLog("翻译进来了")
                vc.translationAlreadyComeNoBuilder()
            }
        }
        
        userList.forEach { user in
            xLog("------ 成员信息 ------")
            xLog("userID：\(user.userID ?? "0")")
            xLog("userName：\(user.userName ?? "空")")
        }
        xLog("加入的成员列表 end")
    }
    
    func onRemoteUserLeave(_ userList: [ZegoUIKitUser]) {
        // 有成员离开了
        guard let vc = callVC else { return }
        xLog("离开的成员列表 begin")
        // 别人是翻译
        if vc.isBuilder {
            // 我是构建者
            // 拿到视频通话中所有得翻译
            let translators = userList.filter { $0.userID == vc.translatorId }
            if translators.count > 0 {
                // 离开的是翻译
                xLog("翻译离开了")
                vc.deleteTranslation()
            }
        } else {
            // 我是被邀请者
            let translators = userList.filter { $0.userID == vc.translatorId }
            if translators.count > 0 {
                // 翻译进来了
                xLog("翻译离开了")
                vc.translationLeaveNoBuilder()
            }
        }
        userList.forEach { user in
            xLog("------ 成员信息 ------")
            xLog("userID：\(user.userID ?? "0")")
            xLog("userName：\(user.userName ?? "空")")
        }
        xLog("离开的成员列表 end")
    }
    
    func onUserCountOrPropertyChanged(_ userList: [ZegoUIKitUser]?) {
        xLog("视频通话中用户数量改变 人数：\(userList?.count ?? 0)")
        callVC?.waitingView?.removeFromSuperview()
    }
    
    func onUserIDUpdated(userID: String) -> String? {
        let userAvatar:String = (ZegoUIKitPrebuiltCallInvitationService.shared.delegate?.onUserIDUpdated?(user: ZegoUIKitUser(userID, "")) ?? "") as String
        return userAvatar as String
    }
    
    func sortAudioVideo(_ userList: [ZegoUIKitUser]) -> [ZegoUIKitUser]? {
        if callVC?.config.layout.mode == .pictureInPicture {
            var tempList: [ZegoUIKitUser] = []
            if userList.count > 1 {
                var index = 0
                for user in userList {
                    let userAvatar:String = (ZegoUIKitPrebuiltCallInvitationService.shared.delegate?.onUserIDUpdated?(user: user) ?? "") as String
                    user.userAvatar = userAvatar
                    if index == 0 {
                        tempList.append(user)
                    } else {
                        if user.userID == ZegoUIKit.shared.localUserInfo?.userID {
                            tempList.append(user)
                        } else {
                            tempList.insert(user, at: 0)
                        }
                    }
                    index = index + 1
                }
            } else {
                for user in userList {
                    let userAvatar:String = (ZegoUIKitPrebuiltCallInvitationService.shared.delegate?.onUserIDUpdated?(user: user) ?? "") as String
                    user.userAvatar = userAvatar
                }
                tempList.append(contentsOf: userList)
            }
            return tempList
        } else {
            var tempList: [ZegoUIKitUser] = userList.reversed()
            var localUser: ZegoUIKitUser?
            var index = 0
            for user in tempList {
                if user.userID == ZegoUIKit.shared.localUserInfo?.userID {
                    localUser = user
                    let userAvatar:String = (ZegoUIKitPrebuiltCallInvitationService.shared.delegate?.onUserIDUpdated?(user: user) ?? "") as String
                    localUser?.userAvatar = userAvatar
                    tempList.remove(at: index)
                    break
                }
                index = index + 1
            }
            if let localUser = localUser {
                if tempList.count == 0 {
                    tempList.append(localUser)
                } else {
                    tempList.insert(localUser, at: 0)
                }
            }
            return tempList
        }
    }
    
    func onOnlySelfInRoom(_ userList: [ZegoUIKitUser]) {
        xLog("房间中只剩我自己了")
        GKCover.hide {
            self.callVC?.delegate?.onOnlySelfInRoom?(userList)
        }
    }
    
    
    // 获取前景窗口
    func getForegroundView(_ userInfo: ZegoUIKitUser?) -> ZegoBaseAudioVideoForegroundView? {
        // 当前用户返回
        guard let userInfo = userInfo, let callVC = self.callVC else {
            return nil
        }
        
        // let foregroundView: ZegoBaseAudioVideoForegroundView? = callVC.delegate?.getForegroundView?(userInfo)
        if let foregroundView = callVC.delegate?.getForegroundView?(userInfo) {
            
            //            let view = UIView()
            //            view.backgroundColor = .green
            //            view.frame = CGRect(x: 10, y: 20, width: 50, height: 50)
            //            view.tag = userInfo.userID?.sg.toInt ?? -1
            //            view.sg.addTapGesture(self, #selector(closeViewAction))
            //            foregroundView.addSubview(view)
            
            return foregroundView
            
        } else {
            // user normal foregroundView
            // 用户正常前景视图,存放用户昵称，
            let normalForegroundView: ZegoCallNormalForegroundView = ZegoCallNormalForegroundView.init(callVC.config, userID: userInfo.userID, frame: .zero)
            normalForegroundView.userInfo = userInfo
            
            //            let view = UIView()
            //            view.backgroundColor = .purple
            //            view.frame = CGRect(x: 30, y: 30, width: 50, height: 50)
            //            view.tag = userInfo.userID?.sg.toInt ?? -1
            //            view.sg.addTapGesture(self, #selector(closeViewAction))
            //            normalForegroundView.addSubview(view)
            
            return normalForegroundView
        }
    }
    
    @objc func closeViewAction(tap: UITapGestureRecognizer) {
        xLog("tag \(tap.view?.tag ?? -1)")
    }
    
    // 收到sei消息
    func onPlayerRecvSEI(_ seiString: String, streamID: String) {
        
        guard let seiDic = seiString.toDictionary(), let model = JsonUtil.modelFromDictionary(seiDic, IMRecvSeiModel.self) else { return }
        
        xLog("收到sei消息 \(seiDic as NSDictionary)")
        
        if model.type == "1" {
            // 根据自定义规则处理文本
            callVC?.oneToOneTranslationView.model = model
            
        } else if model.type == "2" {
            // 此情况为 踢出当前翻译
            // 判断自己是不是翻译
            if model.userId == String(UserProfile.userId) {
                // 踢出的是我自己，退出房间
                callVC?.leaveVideoCall()
            }
            
        } else if model.type == "3" {
            // 邀请翻译，保存翻译 id
            callVC?.translatorId = model.userId
            
        } else if model.type == "4" {
            // 此情况为 翻译主动退出的情况
            callVC?.translatorId = nil
        }
    }
    
    //
    func onAudioVideoAvailable(_ userList: [ZegoUIKitUser]) {
        xLog("onAudioVideoAvailable, 音视频流可用，用户数量: \(userList.count)")
        // 翻译增加进来 关闭实时语音翻译、隐藏添加翻译入口
    }
    
    func onAudioVideoUnavailable(_ userList: [ZegoUIKitUser]) {
        xLog("onAudioVideoUnavailable, 通话视频关闭，用户数量: \(userList.count)")
    }
    
    /// 视频首帧接收回调
    func onPlayerRecvVideoFirstFrame(_ streamID: String) {
        xLog("onPlayerRecvVideoFirstFrame, 收到视频首帧，streamID: \(streamID)")
    }
    
    /// 音频首帧接收回调
    func onPlayerRecvAudioFirstFrame(_ streamID: String) {
        xLog("onPlayerRecvAudioFirstFrame, 收到音频首帧，streamID: \(streamID)")
    }
    
    /// 摄像头状态变化回调
    func onCameraOn(_ user: ZegoUIKitUser, isOn: Bool) {
        xLog("onCameraOn, 用户: \(user.userName ?? ""), 摄像头: \(isOn ? "开启" : "关闭")")
    }
    
    
    /// 用户麦克风状态变化
    /// - 典型应用: 显示麦克风图标状态
    func onMicrophoneOn(_ user: ZegoUIKitUser, isOn: Bool) {
        xLog("[onMicrophoneOn] 用户: \(user.userName ?? ""), isOn: \(isOn ? "开": "关")")
        
        DispatchQueue.global().async {
            /// 异步线程
            if user.userID == String(UserProfile.userId) {
                // 我自己关闭麦克风
                if isOn {
                    NeoNuiManager.shared().startNeoNui()
                } else {
                    NeoNuiManager.shared().pauseNeoNui()
                }
            }
        }
    }
}

extension ZegoUIKitPrebuiltCallVC: ZegoUIKitPrebuiltCallInvitationServiceDelegate {
  
    public func requireConfig(_ data: ZegoCallInvitationData) -> ZegoUIKitPrebuiltCallConfig {
        if data.type == .voiceCall {
            if (data.invitees?.count ?? 0) > 1 {
                let config = ZegoUIKitPrebuiltCallConfig.groupVoiceCall()
                config.topMenuBarConfig.isVisible = true
                config.topMenuBarConfig.buttons = [.minimizingButton]
                return config
            } else {
                let config = ZegoUIKitPrebuiltCallConfig.oneOnOneVoiceCall()
                config.topMenuBarConfig.isVisible = true
                config.topMenuBarConfig.buttons = [.minimizingButton]
                return config
            }
        } else {
            if (data.invitees?.count ?? 0) > 1 {
                let config = ZegoUIKitPrebuiltCallConfig.groupVideoCall()
                config.topMenuBarConfig.isVisible = true
                config.topMenuBarConfig.buttons = [.minimizingButton]
                return config
            } else {
                let config = ZegoUIKitPrebuiltCallConfig.oneOnOneVideoCall()
                config.topMenuBarConfig.isVisible = true
                config.topMenuBarConfig.buttons = [.minimizingButton]
                return config
            }
        }
    }
    // ...

}


class CallCalculateModel: BaseModel {
    // true 通话结束，false 正常通话
    var isCallEnd: Bool = false
    // true：不管 false：积分提示不足
    var isCallTips: Bool = true
}

// MARK: - 音视频通话录制接口调用
extension ZegoUIKitPrebuiltCallVC {
    
    /// 调用音视频通话开始录制接口
    private func callStartRoomRecordingAPI() {
        guard let roomID = self.roomID else {
            xLog("Room ID is empty, skipping start room recording API call")
            return
        }
        
        xLog("Calling start room recording API with roomId: \(roomID)")
        let target = JavaAPI.startRoomRecording(roomId: roomID)
        YLRequest.request(target) { [weak self] result in
            switch result {
            case .success(let res):
                xLog("Start room recording API success: \(res)")
                // 解析返回的taskId
                let dict = res.dataDictionary
                let data = dict["Data"] as? [String: Any]
                let taskId = data?["TaskId"] as? String
                self?.taskId = taskId
                xLog("TaskId: \(String(describing: taskId))")
            case .failure(let err):
                xLog("Start room recording API failed: \(err.errMsg)")
            }
        }
    }
    
    /// 调用音视频通话停止录制接口
    private func callStopRecordingAPI() {
        guard let taskId = self.taskId else {
            xLog("TaskId is empty, skipping stop recording API call")
            return
        }
        
        xLog("Calling stop recording API with taskId: \(taskId)")
        let target = JavaAPI.stopRecording(taskId: taskId)
        YLRequest.request(target) { result in
            switch result {
            case .success(let res):
                xLog("Stop recording API success: \(res)")
            case .failure(let err):
                xLog("Stop recording API failed: \(err.errMsg)")
            }
        }
    }
}
